#include "NetComm.h"
#include "CommonShare.h"
#include "CJsonObject.hpp"
#include "Singleton.h"

#include <string>
#include <algorithm>


// HTTP cmd
#include "HttpCmdGetDateTime.h"
#include "HttpCmdGetIfconfig.h"
#include "HttpCmdGetShellResult.h"
#include "HttpCmdGetCmdList.h"
#include "HttpCmdGetVersion.h"
#include "HttpCmdGetSqliteData.h"
#include "HttpCmdTestSqlite.h"

#include "Log_Client.h"

using namespace std;



CNetComm::CNetComm()
{
    // Register the handlers for each command.
    m_httpCmdObjMap["GetDateTime"]      = CSingleton<CHttpCmdGetDateTime>::GetInstance();
    m_httpCmdObjMap["GetIfconfig"]      = CSingleton<CHttpCmdGetIfconfig>::GetInstance();
    m_httpCmdObjMap["GetShellResult"]   = CSingleton<CHttpCmdGetShellResult>::GetInstance();
    m_httpCmdObjMap["GetCmdList"]       = CSingleton<CHttpCmdGetCmdList>::GetInstance();
    m_httpCmdObjMap["GetVersion"]       = CSingleton<CHttpCmdGetVersion>::GetInstance();
    m_httpCmdObjMap["GetSqliteData"]    = CSingleton<CHttpCmdGetSqliteData>::GetInstance();
    m_httpCmdObjMap["TestSqlite"]       = CSingleton<CHttpCmdTestSqlite>::GetInstance();

    
    for (int i = 0; i < CLIENT_MAX_NUM; i++)
    {
        g_SocketStuArray[i].socketFd = -1;
    }
}
CNetComm::~CNetComm()
{
    for (int i = 0; i < CLIENT_MAX_NUM; i++)
    {
        g_SocketStuArray[i].socketFd = -1;
    }
}

// Thread Handler, purpose: reverse the data and send back.
void *CNetComm::TcpHandler_Reverse_RoutineFunc(void *arg)
{
    printf("Function Name = %s()\n", __func__);

    const char* cstrThreadName = "NetReverse";
    if (pthread_setname_np(pthread_self(), cstrThreadName) != 0) 
    {
        printf("Error setting thread name to %s\n", cstrThreadName);
    }


    pthread_detach(pthread_self());
    int socketFd = *((int *)arg);
    char recvBuf[1024];
    while (1)
    {
        memset(recvBuf, 0, sizeof(recvBuf));
        if (read(socketFd, recvBuf, sizeof(recvBuf)) <= 0)
        {
            printf("%s read error\n", __func__);
            close(socketFd);
            break;
        }
        else
        {
            // printf("recv %s from client\n",recvBuf);
            string strRecvBuf(recvBuf);
            string strSendBuf = strRecvBuf;

            // 反转后发回去
            reverse(strSendBuf.begin(), strSendBuf.end());
            // printf("strRecvBuf = %s  , strSendBuf = %s\n",strRecvBuf.c_str(),strSendBuf.c_str());
            write(socketFd, strSendBuf.c_str(), strSendBuf.length());
        }
    }

    // CloseClient(&socketFd);
}

bool CNetComm::HttpBasicAuth(const std::string &strAll)
{
    std::string authStartWord = "Authorization: Basic ";
    size_t json_start = strAll.find(authStartWord)+ authStartWord.length(); // Not include '/'
    std::string strSubData = strAll.substr(json_start);  // strSubData 一直截取到最后一个
    
    // 过滤掉前面的，从后面开始找
    json_start = 0;
    size_t json_end = strSubData.find("\r\n") - 1;
    if (json_end == string::npos || json_end < json_start) 
    {
        printf("Error: Invalid JSON request. strAll = %s\n", strAll.c_str());
        return -1;
    }
    // authData = "admin:password"  Base64 Data
    std::string authData = strSubData.substr(json_start, json_end - json_start + 1);


    std::string stdCorrectBase64 = "YWRtaW46MTIzNDU2";

    bool ret = true;
    if(authData != stdCorrectBase64)
    {
        ret = false;
        printf("auth error\n  authData=%s\n  stdCorrectBase64 = %s\n", authData.c_str(), stdCorrectBase64.c_str());
    }


    return ret;
}

// Parse the HTTP Message to get the "Request Cmd".
bool CNetComm::GetHttpReqCmd(const string &strAll, string &strCmd)
{
    // Parse the HTTP Request
    size_t json_start = strAll.find("/")+1; // Not include '/'
    size_t json_end = strAll.find(" HTTP/") - 1;
    if (json_start == string::npos || json_end == string::npos || json_end < json_start) 
    {
        printf("Error: Invalid JSON request. strAll = %s\n", strAll.c_str());
        return -1;
    }
    strCmd = strAll.substr(json_start, json_end - json_start + 1);
    // printf("------------Server recv CMD = \n%s\n", strCmd.c_str());

    return true;
}

// Parse the HTTP Message to get the "Request Data".
bool CNetComm::GetHttpReqData(const string &strAll, string &strBody)
{
    // Parse the HTTP Request
    size_t json_start = strAll.find("{");
    size_t json_end = strAll.rfind("}");
    if (json_start == string::npos || json_end == string::npos || json_end < json_start)
    {
        // printf("do not have valid request body,it is OK too\n");
        return false;
    }
    strBody = strAll.substr(json_start, json_end - json_start + 1);
    // printf("------------Server recv data = \n%s\n", strBody.c_str());

    return true;
}

// Build the response in HTTP format
bool CNetComm::AssemblyHttpResponse(const string &strBody, string &strAll)
{
    // Send HTTP Response
    strAll.clear();
    strAll += "HTTP/1.1 200 OK\r\n";
    strAll += "Content-Type: application/json\r\n";
    strAll += string("Content-Length: ") + std::to_string(strBody.length()) + "\r\n";
    strAll += "Connection: close\r\n";
    strAll += "\r\n";
    strAll += strBody;

    return true;
}

// Routine to handle HTTP request
// 只处理一次，处理完，会把套接字关闭，不会长久保存连接
int CNetComm::TcpHandler_HttpServer_RoutineFunc(int httpServerFd)
{
    printf("HTTP Server Running\n");

    // Accept the Connection
    struct sockaddr_in client_address;
    socklen_t client_address_length = sizeof(client_address);
    int client_socket = accept(httpServerFd, (struct sockaddr *)&client_address, &client_address_length);
    if (client_socket == -1)
    {
        AddLog(ERROR_LEVEL, "Network", "Error: Failed to accept connection");
        return -1;
    }

    // Accept the Request
    string strRequest;
    char buffer[1024 * 10];
    int bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);
    // printf("Original Data Recieved is :\n===============\n%s\n==================\n", buffer);
    while (bytes_received > 0)
    {
        strRequest += buffer;
        if (strstr(buffer, "\r\n\r\n"))
        {
            break;
        }
    }
    if (bytes_received == -1)
    {
        AddLog(ERROR_LEVEL, "Network", "Failed to receive HTTP request");
        close(client_socket);
        return -1;
    }

    string strRspData;
    do {
        // Auth 校验如果出错，直接返回给客户端
        if (!HttpBasicAuth(strRequest))
        {
            // Auth 校验出错
            neb::CJsonObject resJsonObj;
            resJsonObj.Add("status", "Error");
            std::string strRspErrorMessage = "Auth Base64 Error";
            resJsonObj.Add("message", strRspErrorMessage);
            strRspData = resJsonObj.ToString();

            break;
        }

        CHttpCmdBase *cmdObj = NULL;
        string strCmd;

        GetHttpReqCmd(strRequest, strCmd);            
        std::map<std::string, CHttpCmdBase*>::iterator iter  = m_httpCmdObjMap.find(strCmd);
        if (iter == m_httpCmdObjMap.end())
        {
            // 命令出错
            neb::CJsonObject resJsonObj;
            resJsonObj.Add("status", "Error");
            std::string strRspErrorMessage = strCmd + " is a Invalid Command";
            resJsonObj.Add("message", strRspErrorMessage);
            strRspData = resJsonObj.ToString();

            AddLog(DEBUG_LEVEL, "Http Server", "ERROR Recv a Invalid Request cmd = %s", strCmd.c_str());

            break;
        }
        
        AddLog(DEBUG_LEVEL, "Http Server", "Recv a Request cmd = %s", strCmd.c_str());

        // 不一定需要Request的Body，可以没有附带的Body报文
        string strReqData;
        GetHttpReqData(strRequest, strReqData);

        cmdObj = iter->second;
        // 找到对应处理函数，得到响应信息
        cmdObj->HttpCmdHandlerFunc(strReqData, strRspData);
        
    } while(0);
    
    std::string strRespondAllMessage;
    AssemblyHttpResponse(strRspData, strRespondAllMessage);

    if (send(client_socket, strRespondAllMessage.c_str(), strRespondAllMessage.length(), 0) == -1)
    {
        printf("Error: Failed to send HTTP response.\n");
        close(client_socket);
        return -1;
    }

    // 关闭连接
    close(client_socket);
}

void *CNetComm::ThreadFunc(void *arg)
{
    int socketFd = *((int *)arg);
    int ret = 0;

#if 1
    char buf[50];
    strncpy(buf, "Hello Qt,I am 6818 from Polaris", sizeof(buf));
    printf(" NetComm::threadFunc()  buf = %s\n", buf);
    write(socketFd, buf, sizeof(buf));
#endif

    // pthread_t p;
    // pthread_create(&p,NULL,threadRecvFunc,(void *)&socketFd);

    while (1)
    {
        memset(m_recvBuf, 0, sizeof(m_recvBuf));
        if ((ret = read(socketFd, m_recvBuf, sizeof(m_recvBuf))) < 0)
        {
            printf("%s read error\n", __func__);
            close(socketFd);
            break;
        }
        else if (ret == 0)
        {
            printf("%s read ret == 0\n", __func__);
            close(socketFd);
            break;
        }
        else
        {
            // 至少有两个字节
            if (ret < 2)
            {
                printf("[error] recv bytes less than 2,just %d\n", ret);
            }
            int size = m_recvBuf[1];
            // 先校验大小是否正确，不正确直接 continue
            if (ret != size + 2)
            {
                printf("[error] get the wrong size of data,should get %d(bytes) also %c ,actually get %d(bytes) ,continue\n", size, size, ret);
                continue;
            }
            printf("recv buf[0] = 0x%x , buf[1] = %d\n", m_recvBuf[0], m_recvBuf[1]);
            printf("recv [actually get size = %d] data = %s\n", ret, m_recvBuf + 2);
            ServerBaseFunc();
            SendDataBySocket(socketFd);
        }
    }

    // while(1)
    // {
    //     sleep(1);
    //     tmp_ArmSysInfo = (ArmSysInfo *)get_pShmArmSysInfo();
    //     snprintf(buf,400,"%s@%s@%s",tmp_ArmSysInfo->IpAddr,tmp_ArmSysInfo->DevName,tmp_ArmSysInfo->DevtimeStr);
    //     // 对面一旦退出，感觉这个会杀死进程，打印出来看看，并且要做出错处理！
    //     int ret = write(socketFd,buf,sizeof(buf));
    //     if(ret<0)
    //     {
    //         printf("[%s] --- write() error return\n",__func__);
    //         pthread_cancel(p);
    //         return NULL;
    //     }
    // }
}

// 此函数根据接收到的命令，解析帧
// 将要发送的数据装入发送缓冲区中
void CNetComm::ServerBaseFunc()
{
    char commandType = m_recvBuf[0];
    printf("commandType = 0x%x\n", commandType);
    switch (commandType)
    {
    case CASUAL_STR:
        printf("[rece Str]:%s\n", m_recvBuf + 2);
        m_NoAckFlag = true;
        break;
    case DEVICE_NAME:
    case DEVICE_TIME:
        LoadDeviceParam(commandType);
        break;
    default:
        m_NoAckFlag = true;
        printf("[error] can not find this CommandType(%d)\n", commandType);
        break;
    }

    printf("finish %s\n", __func__);
}

void CNetComm::LoadDeviceParam(char commandType)
{
    m_NoAckFlag = false;
    m_sendBuf[0] = commandType;

#if 0
    // NetComm.cpp:(.text+0x12d3)：对‘get_pShmArmSysInfo()’未定义的引用
    ArmSysInfo *tmp_ArmSysInfo = (ArmSysInfo *)get_pShmArmSysInfo();
    printf("[LoadDeviceParam()] commandType = 0x%x\n", commandType);
    memset(m_sendBuf, 0, sizeof(m_sendDataSize));
    switch (commandType)
    {
    case DEVICE_NAME:
        m_sendDataSize = strlen(tmp_ArmSysInfo->DevName);
        printf("NAME --- m_sendDataSize = %d\n", m_sendDataSize);
        printf("tmp_ArmSysInfo->DevName = %s\n", tmp_ArmSysInfo->DevName);
        m_sendBuf[1] = m_sendDataSize;
        strncpy(m_sendBuf + 2, tmp_ArmSysInfo->DevName, m_sendDataSize);
        break;
    case DEVICE_TIME:
        m_sendDataSize = strlen(tmp_ArmSysInfo->DevtimeStr);
        printf("TIME --- m_sendDataSize = %d\n", m_sendDataSize);
        printf("tmp_ArmSysInfo->DevtimeStr = %s\n", tmp_ArmSysInfo->DevtimeStr);
        m_sendBuf[1] = m_sendDataSize;
        strncpy(m_sendBuf + 2, tmp_ArmSysInfo->DevtimeStr, m_sendDataSize);
        m_sendBuf[m_sendDataSize] = '\0';
        // m_sendBuf[m_sendDataSize+2] = '\0';
        printf("last  strlen(m_sendBuf) = %lu\n", strlen(m_sendBuf));
    default:
        break;
    }
    printf("LoadDeviceParam finish \n");
#endif
}

// 将发送缓冲区中的数据发出
void CNetComm::SendDataBySocket(int socketFd)
{
    printf("SocketSendData() begin--\n");
    if (true == m_NoAckFlag)
    {
        return;
    }
    printf("[%s] m_sendDataSize = %d  sizeof(m_sendBuf) = %lu strlen(m_sendBuf) = %lu\n", __func__, m_sendDataSize, sizeof(m_sendBuf), strlen(m_sendBuf));
    // bug
    printf("sendBuf = %s\n", m_sendBuf);

    int ret = write(socketFd, m_sendBuf, m_sendDataSize + 2);
    if (ret < 0)
    {
        printf("[%s] --- write() error return\n", __func__);
        return;
    }
    else
    {
        printf("after write()  m_sendDataSize+2 = %d   ret = %d\n", m_sendDataSize + 2, ret);
    }
    //"Thu Jan  1 00:05:39 2015"
}


bool CNetComm::AddNewClient(const int *newClientFd)
{
    for (int i = 0; i < CLIENT_MAX_NUM; i++)
    {
        if (g_SocketStuArray[i].socketFd == -1)
        {
            g_SocketStuArray[i].socketFd = *newClientFd;
            return true;
        }
    }

    printf("up to limitation, max is %d\n", CLIENT_MAX_NUM);
    return false;
}

void CNetComm::CloseClient(const int *ClientFd)
{
    for (int i = 0; i < CLIENT_MAX_NUM; i++)
    {
        if (g_SocketStuArray[i].socketFd == *ClientFd)
        {
            close(g_SocketStuArray[i].socketFd);
            g_SocketStuArray[i].socketFd = -1;
            g_SocketStuArray[i].tid = -1;
            return;
        }
    }
}

void CNetComm::CloseAllClient()
{
    for (int i = 0; i < CLIENT_MAX_NUM; i++)
    {
        close(g_SocketStuArray[i].socketFd);
        g_SocketStuArray[i].socketFd = -1;
        g_SocketStuArray[i].tid = -1;
    }
}

int CNetComm::TcpServerHttp()
{
    int nPort = 1998;
    int socketFd = SocketInit(nPort);
    if (socketFd == -1)
    {
        printf("TcpServerHttp() socketInit() == -1 \n");
        return -1;
    }

    while (1)
    {
        TcpHandler_HttpServer_RoutineFunc(socketFd);
    }
}


int CNetComm::SocketInit(int port)
{
    //================socket()
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1)
    {
        printf("socket error\n");
        return -1;
    }

    // 设置端口复用 ( bind error 发生这种情况是在服务端主动关闭连接以后，接着立即启动就会出现这种错误)
    int opt = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));

    // 定义一个结构体来存放服务器（本机）的信息，用于bind()
    struct sockaddr_in ipAddr;
    bzero(&ipAddr, sizeof(ipAddr));

    ipAddr.sin_family = AF_INET;                // 协议
    ipAddr.sin_port = htons(port);              // 端口号
    ipAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 本机的IP地址

    //================bind()
    int ret = bind(fd, (struct sockaddr *)&ipAddr, sizeof(ipAddr));
    if (ret == -1)
    {
        printf("bind error\n");
        return -1;
    }

    //================listen()
    ret = listen(fd, 0);
    if (ret == -1)
    {
        printf("bind error\n");
        return -1;
    }

    return fd;
}
